package base.thread_JUC.demo01;

/**
 * volatile 不能保证原子性。
 * why？
 * 解释：假设t1，t2两线程情况，初始同时读取num=0进自己工作内存。后面开始执行以下步骤：
 * t1执行操作，操作完毕后会将t1自己工作内存结果写入主存，此时主存num=1；
 * 这时，t2也执行完毕，也将num=1写入主存，因t2对t1执行结果不可见，没有检查机制，直接覆盖了t1结果。
 * t1和t2两次执行结果后，主存中num=1，而不是2，这就导致多线程环境下不能保证原子性操作num，而是t1在操作num的同时，
 * t2也在操作num。（如果t2有检查机制，在写回主存前，先检查一遍主存num值是否有变化，有就重新读取主存中num值，然后继续执行num++，
 * 再次准备写回主存，写回之前依旧执行上述检查步骤，直到检查出自己t2在num++前后，主存num值一致，就写回主存，这种机制就叫自旋算法CAS）
 * volatile + CAS（比较并设置） 就是JUC里面的 java.util.concurrent.atomic.Atomicxxx API的原理。volatile 保证了内存可见性，
 * CAS（Compare-And-Swap） 算法保证数据的原子性。
 * CAS算法是硬件对于并发操作共享数据的支持（原子支持）
 * CAS包含了三个操作数：
 * 内存值 V
 * 预估值 A（先前读到的内存数据）
 * 更新值 B
 * 当且仅当 V==A时，V=B，否则，不做任何操作。
 *
 */
public class Client3 {

    // volatile 不能保证原子性
    volatile int num=0;
    public void add(){
        num++;
    }

    public static void main(String[] args) {
        Client3 c3 = new Client3();

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    c3.add();
                }
            },String.valueOf(i)).start();
        }

        /**
         * 线程组里面除了main线程还有GC线程在执行，因此判断>2,说明上面20个线程还没执行完，
         * 那么main线程继续等待（礼让调度）。
         */
        while (Thread.activeCount()>2){
            Thread.yield();//礼让调度
        }

        System.out.println(Thread.currentThread().getName()+" main "+c3.num);
    }

}
